This is an R Markdown
Notebook. When you execute code within the notebook, the results appear
beneath the code.
Try executing this chunk by clicking the Run button within
the chunk or by placing your cursor inside it and pressing
Cmd+Shift+Enter.
Install packages
# install.packages("readr")
# install.packages("dplyr")
# install.packages("stringr")
# install.packages("shiny")
# install.packages("ggplot2")
# install.packages("plotly")
Load in packages
# Allows us to read-in csv files
library(readr)
# For data manipulation
library(dplyr)
Attaching package: ‘dplyr’
The following objects are masked from ‘package:stats’:
filter, lag
The following objects are masked from ‘package:base’:
intersect, setdiff, setequal, union
# For regular expression operations
library(stringr)
# library(shiny)
library(ggplot2)
# Used tp create interactive visualisations
library(plotly)
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
Load-in dataset
df <- read_csv('Data/GI_age.csv')
Rows: 42 Columns: 7── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (4): England and Wales Code, England and Wales, Gender identity (7 categories), Age (6 categories)
dbl (3): Gender identity (7 categories) Code, Age (6 categories) Code, Observation
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Brief glimpse of data structure
# But can also click on the dataset in the Environment pane
head(df, 10)
# Let's check out the dimensions
dim(df)
[1] 42 7
Data Cleaning
# str_replace_all() method finds all substrings which match the regex and replaces them with empty string
# First, let's replace any brackets with empty strings
colnames(df) <- str_replace_all(colnames(df), "\\s*\\([^)]*\\)", "")
# Lowercase column text and replace empty spaces with "_"
colnames(df) <- tolower(colnames(df))
colnames(df) <- str_replace_all(colnames(df), " ", "_")
# Let's see if it worked..
head(df)
Pipes and other operators..
So, we’ve already come across the assignment operator ‘<-’ which
is used to assign a value. E.g. df <- read_csv(‘Data/GI_age.csv’),
here we assign our csv file to a dataframe variable called ‘df’.
But, we’re now going to encounter the pipe operator ‘%>%’ which
can seem intimidating at first but is actually pretty simple. It’s used
to pass the result of one function directly into the next function. E.g.
df <- df %>% filter(gender_identity_code != -8), here we start
with our df and pass it to the filter function using the pipe operator.
This basically supplies the filter() function with its first argument,
which is the dataframe to filter on. And here we encounter a logical
operator ‘!=’ within the filter() function, which specifies that we
should only keep rows where gender_identity_code is not equal to -8.
# Get rid of columns with 0 observations
df <- df %>%
filter(gender_identity_code != -8)
# Check it worked
head(df, 10)
# Get rid of redundant age category
# Further filter data
df <- df %>%
filter(age_code != 1)
# Clean up the values in the 'age' column. Let's shorten them.
# Chain str_replace() calls together to apply multiple string replacements in succession
# Each str_replace() call is applied to the result of the previous one
df$age <- df$age %>%
str_replace('Aged ', '') %>%
str_replace('to', '-') %>%
str_replace('years', '') %>%
str_replace('and over', '+')
# We can pass our df to the select function, where we specify the column we're interested in.
# Then, we pipe the output to the head function.
df %>%
select(age) %>%
head()
Question
How is gender identity distributed among different age groups?
Some subquestions that this can help us answer:
- What % of trans men are aged 16-24 years?
- Are older age groups overrepresented in the ‘non-response’
category?
Data pre-processing
Calculate percentages
Below, we use the group_by function to group the data by
‘gender_identity’ and calculate the percentage within each group. Then
the mutate() function adds a new column ‘percentage’ to df, which (for
each group) divides the observation by the sum of observations,
multiplies it by 100, and rounds it up to 2 decimal points. We then use
the ungroup function when we’re done with the grouping operation.
df <- df %>%
group_by(gender_identity) %>%
mutate(percentage = round((observation / sum(observation) * 100), 2)) %>%
ungroup()
head(df)
Interactive grouped bar chart + stacked bar chart
So, the convention when using Plotly in R, is to create our plot
first by using the ggplot2 package. Then, we convert the ggplot object
to a ‘plotly’ object using ‘ggplotly’. There’s a lot going on here so
I’ll break some of it down. The ggplot() function initialises a ggplot
object, which sets up the dataframe that will be used for the plot and
specifies the aesthetic mappings which describe how variables in the
data are mapped to visual properties. So, inside aes() we specify our x
and y columns, and specify that we want to map our age column to fill
the colour of the bars.
Meanwhile, geom_bar() is used to make bar charts, so it adds the bar
geometry to the plot. And we set stat to ‘identity’, which tells
‘ggplot’ to use the value in the y-axis column (‘percentage’) for the
height of the bars. By setting position to ‘dodge’ we ensure that the
bars are placed next to each other.
Finally, labs() is used to add or modify labels, and theme is used to
customise non-data parts of the plot like text, legend, axes. And
scale_fill_discrete() controls the colour scales and here we use the
name parameter to label our legend “Age”.
TLDR: we’re using the + operator and ggplot functions to build upon
the base ggplot object, layering on aesthetic mappings, geometries,
labels, etc.
p <- ggplot(df, aes(x = gender_identity, y = percentage, fill = age,
text = paste('Observation:', observation))) + # Include observation info
geom_bar(stat = "identity", position = "dodge") +
labs(title = 'Distribution of Gender Identity Categories Among Age Groups',
x = 'Gender Identity', y = 'Percentage') +
theme(plot.title = element_text(hjust = 0.5)) +
scale_fill_discrete(name = "Age")
# Let's take a look at our static graph
p

Hmm okay. Not too shabby, but we’re definitely going to have to do
something about our x-axis labels, as right now everything is pretty
cluttered. Maybe we could rotate them, or just rename them. We’ll get
round to it. But for now, let’s make this thing interactive.
# Convert ggplot object to a plotly object for interactivity
fig <- ggplotly(p, tooltip = c("y", "fill", "text")) # Specify tooltip components
# Let's check it out
fig
Tooltips
When using different R libraries geared towards interactive
visualisations, you’ll often come across ‘tooltips’. These are small
boxes that provide information when a user hovers over a part of a data
visualisation such as: a point on a graph, a bar in a bar chart, or a
segment in a pie chart. They are used to display additional information
about the data point or object, providing more context without
cluttering up the chart.
# Set the levels of the factor to the order you want them to appear
df$gender_identity <- factor(df$gender_identity, levels = c(
"Gender identity the same as sex registered at birth",
"Gender identity different from sex registered at birth but no specific identity given",
"Trans woman",
"Trans man",
"All other gender identities",
"Not answered"
))
# Generate the plotly figure
fig <- ggplotly(p)
# Specify custom tick labels with the corresponding tick values
fig <- fig %>%
layout(
title = list(text = 'Distribution of Gender Identity Categories Among Age Groups', x = 0.5),
xaxis = list(
title = 'Gender Identity',
tickvals = levels(df$gender_identity), # Set tickvals to factor levels
ticktext = c(
"Cisgender",
"Gender identity different from sex",
"Trans woman",
"Trans man",
"All other identities",
"Not answered"
)
),
yaxis = list(title = 'Percentage'),
legend = list(orientation = "v", yanchor = "top", y = -0.3, xanchor = "center", x = 1)
)
fig
Add a new chunk by clicking the Insert Chunk button on the
toolbar or by pressing Cmd+Option+I.
When you save the notebook, an HTML file containing the code and
output will be saved alongside it (click the Preview button or
press Cmd+Shift+K to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the
editor. Consequently, unlike Knit, Preview does not
run any R code chunks. Instead, the output of the chunk when it was last
run in the editor is displayed.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgojIyBJbnN0YWxsIHBhY2thZ2VzCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJyZWFkciIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQojIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKQojIGluc3RhbGwucGFja2FnZXMoInNoaW55IikKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQpgYGAKCiMjIExvYWQgaW4gcGFja2FnZXMKCmBgYHtyfQojIEFsbG93cyB1cyB0byByZWFkLWluIGNzdiBmaWxlcwpsaWJyYXJ5KHJlYWRyKSAKIyBGb3IgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeShkcGx5cikgCiMgRm9yIHJlZ3VsYXIgZXhwcmVzc2lvbiBvcGVyYXRpb25zIApsaWJyYXJ5KHN0cmluZ3IpIAojIGxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoZ2dwbG90MikKIyBVc2VkIHRwIGNyZWF0ZSBpbnRlcmFjdGl2ZSB2aXN1YWxpc2F0aW9ucwpsaWJyYXJ5KHBsb3RseSkKYGBgCiMjIExvYWQtaW4gZGF0YXNldAoKYGBge3J9CmRmIDwtIHJlYWRfY3N2KCdEYXRhL0dJX2FnZS5jc3YnKQpgYGAKYGBge3J9CiMgQnJpZWYgZ2xpbXBzZSBvZiBkYXRhIHN0cnVjdHVyZQojIEJ1dCBjYW4gYWxzbyBjbGljayBvbiB0aGUgZGF0YXNldCBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZQpoZWFkKGRmLCAxMCkKYGBgCgpgYGB7cn0KIyBMZXQncyBjaGVjayBvdXQgdGhlIGRpbWVuc2lvbnMKCmRpbShkZikKYGBgCgojIyBEYXRhIENsZWFuaW5nCgpgYGB7cn0KIyBzdHJfcmVwbGFjZV9hbGwoKSBtZXRob2QgZmluZHMgYWxsIHN1YnN0cmluZ3Mgd2hpY2ggbWF0Y2ggdGhlIHJlZ2V4IGFuZCByZXBsYWNlcyB0aGVtIHdpdGggZW1wdHkgc3RyaW5nCiMgRmlyc3QsIGxldCdzIHJlcGxhY2UgYW55IGJyYWNrZXRzIHdpdGggZW1wdHkgc3RyaW5ncwpjb2xuYW1lcyhkZikgPC0gc3RyX3JlcGxhY2VfYWxsKGNvbG5hbWVzKGRmKSwgIlxccypcXChbXildKlxcKSIsICIiKQoKIyBMb3dlcmNhc2UgY29sdW1uIHRleHQgYW5kIHJlcGxhY2UgZW1wdHkgc3BhY2VzIHdpdGggIl8iCmNvbG5hbWVzKGRmKSA8LSB0b2xvd2VyKGNvbG5hbWVzKGRmKSkKY29sbmFtZXMoZGYpIDwtIHN0cl9yZXBsYWNlX2FsbChjb2xuYW1lcyhkZiksICIgIiwgIl8iKQoKIyBMZXQncyBzZWUgaWYgaXQgd29ya2VkLi4KaGVhZChkZikKYGBgCgojIyMgUGlwZXMgYW5kIG90aGVyIG9wZXJhdG9ycy4uCgpTbywgd2UndmUgYWxyZWFkeSBjb21lIGFjcm9zcyB0aGUgYXNzaWdubWVudCBvcGVyYXRvciAnPC0nIHdoaWNoIGlzIHVzZWQgdG8gYXNzaWduIGEgdmFsdWUuIEUuZy4gZGYgPC0gcmVhZF9jc3YoJ0RhdGEvR0lfYWdlLmNzdicpLCBoZXJlIHdlIGFzc2lnbiBvdXIgY3N2IGZpbGUgdG8gYSBkYXRhZnJhbWUgdmFyaWFibGUgY2FsbGVkICdkZicuCgpCdXQsIHdlJ3JlIG5vdyBnb2luZyB0byBlbmNvdW50ZXIgdGhlIHBpcGUgb3BlcmF0b3IgJyU+JScgd2hpY2ggY2FuIHNlZW0gaW50aW1pZGF0aW5nIGF0IGZpcnN0IGJ1dCBpcyBhY3R1YWxseSBwcmV0dHkgc2ltcGxlLiBJdCdzIHVzZWQgdG8gcGFzcyB0aGUgcmVzdWx0IG9mIG9uZSBmdW5jdGlvbiBkaXJlY3RseSBpbnRvIHRoZSBuZXh0IGZ1bmN0aW9uLiBFLmcuIGRmIDwtIGRmICU+JSBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgpLCBoZXJlIHdlIHN0YXJ0IHdpdGggb3VyIGRmIGFuZCBwYXNzIGl0IHRvIHRoZSBmaWx0ZXIgZnVuY3Rpb24gdXNpbmcgdGhlIHBpcGUgb3BlcmF0b3IuIFRoaXMgYmFzaWNhbGx5IHN1cHBsaWVzIHRoZSBmaWx0ZXIoKSBmdW5jdGlvbiB3aXRoIGl0cyBmaXJzdCBhcmd1bWVudCwgd2hpY2ggaXMgdGhlIGRhdGFmcmFtZSB0byBmaWx0ZXIgb24uIEFuZCBoZXJlIHdlIGVuY291bnRlciBhIGxvZ2ljYWwgb3BlcmF0b3IgJyE9JyB3aXRoaW4gdGhlIGZpbHRlcigpIGZ1bmN0aW9uLCB3aGljaCBzcGVjaWZpZXMgdGhhdCB3ZSBzaG91bGQgb25seSBrZWVwIHJvd3Mgd2hlcmUgZ2VuZGVyX2lkZW50aXR5X2NvZGUgaXMgbm90IGVxdWFsIHRvIC04LiAKCmBgYHtyfQojIEdldCByaWQgb2YgY29sdW1ucyB3aXRoIDAgb2JzZXJ2YXRpb25zCmRmIDwtIGRmICU+JSAKICBmaWx0ZXIoZ2VuZGVyX2lkZW50aXR5X2NvZGUgIT0gLTgpIAoKIyBDaGVjayBpdCB3b3JrZWQKCmhlYWQoZGYsIDEwKQpgYGAKCmBgYHtyfQojIEdldCByaWQgb2YgcmVkdW5kYW50IGFnZSBjYXRlZ29yeQojIEZ1cnRoZXIgZmlsdGVyIGRhdGEKZGYgPC0gZGYgJT4lCiAgZmlsdGVyKGFnZV9jb2RlICE9IDEpCgpgYGAKCmBgYHtyfQojIENsZWFuIHVwIHRoZSB2YWx1ZXMgaW4gdGhlICdhZ2UnIGNvbHVtbi4gTGV0J3Mgc2hvcnRlbiB0aGVtLgoKIyBDaGFpbiBzdHJfcmVwbGFjZSgpIGNhbGxzIHRvZ2V0aGVyIHRvIGFwcGx5IG11bHRpcGxlIHN0cmluZyByZXBsYWNlbWVudHMgaW4gc3VjY2Vzc2lvbgojIEVhY2ggc3RyX3JlcGxhY2UoKSBjYWxsIGlzIGFwcGxpZWQgdG8gdGhlIHJlc3VsdCBvZiB0aGUgcHJldmlvdXMgb25lCmRmJGFnZSA8LSBkZiRhZ2UgJT4lCiAgc3RyX3JlcGxhY2UoJ0FnZWQgJywgJycpICU+JQogIHN0cl9yZXBsYWNlKCd0bycsICctJykgJT4lCiAgc3RyX3JlcGxhY2UoJ3llYXJzJywgJycpICU+JQogIHN0cl9yZXBsYWNlKCdhbmQgb3ZlcicsICcrJykKCiMgV2UgY2FuIHBhc3Mgb3VyIGRmIHRvIHRoZSBzZWxlY3QgZnVuY3Rpb24sIHdoZXJlIHdlIHNwZWNpZnkgdGhlIGNvbHVtbiB3ZSdyZSBpbnRlcmVzdGVkIGluLgojIFRoZW4sIHdlIHBpcGUgdGhlIG91dHB1dCB0byB0aGUgaGVhZCBmdW5jdGlvbi4KZGYgJT4lCiAgc2VsZWN0KGFnZSkgJT4lCiAgaGVhZCgpCmBgYAoKIyMgUXVlc3Rpb24KCkhvdyBpcyBnZW5kZXIgaWRlbnRpdHkgZGlzdHJpYnV0ZWQgYW1vbmcgZGlmZmVyZW50IGFnZSBncm91cHM/CgpTb21lIHN1YnF1ZXN0aW9ucyB0aGF0IHRoaXMgY2FuIGhlbHAgdXMgYW5zd2VyOgoKKiBXaGF0ICUgb2YgdHJhbnMgbWVuIGFyZSBhZ2VkIDE2LTI0IHllYXJzPwoqIEFyZSBvbGRlciBhZ2UgZ3JvdXBzIG92ZXJyZXByZXNlbnRlZCBpbiB0aGUgJ25vbi1yZXNwb25zZScgY2F0ZWdvcnk/CgojIyBEYXRhIHByZS1wcm9jZXNzaW5nCgojIyMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzIAoKQmVsb3csIHdlIHVzZSB0aGUgZ3JvdXBfYnkgZnVuY3Rpb24gdG8gZ3JvdXAgdGhlIGRhdGEgYnkgJ2dlbmRlcl9pZGVudGl0eScgYW5kIGNhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSB3aXRoaW4gZWFjaCBncm91cC4gVGhlbiB0aGUgbXV0YXRlKCkgZnVuY3Rpb24gYWRkcyBhIG5ldyBjb2x1bW4gJ3BlcmNlbnRhZ2UnIHRvIGRmLCB3aGljaCAoZm9yIGVhY2ggZ3JvdXApIGRpdmlkZXMgdGhlIG9ic2VydmF0aW9uIGJ5IHRoZSBzdW0gb2Ygb2JzZXJ2YXRpb25zLCBtdWx0aXBsaWVzIGl0IGJ5IDEwMCwgYW5kIHJvdW5kcyBpdCB1cCB0byAyIGRlY2ltYWwgcG9pbnRzLiBXZSB0aGVuIHVzZSB0aGUgdW5ncm91cCBmdW5jdGlvbiB3aGVuIHdlJ3JlIGRvbmUgd2l0aCB0aGUgZ3JvdXBpbmcgb3BlcmF0aW9uLiAKCmBgYHtyfQpkZiA8LSBkZiAlPiUKICBncm91cF9ieShnZW5kZXJfaWRlbnRpdHkpICU+JQogIG11dGF0ZShwZXJjZW50YWdlID0gcm91bmQoKG9ic2VydmF0aW9uIC8gc3VtKG9ic2VydmF0aW9uKSAqIDEwMCksIDIpKSAlPiUKICB1bmdyb3VwKCkKCmhlYWQoZGYpCmBgYAoKIyMgSW50ZXJhY3RpdmUgZ3JvdXBlZCBiYXIgY2hhcnQgKyBzdGFja2VkIGJhciBjaGFydAoKU28sIHRoZSBjb252ZW50aW9uIHdoZW4gdXNpbmcgUGxvdGx5IGluIFIsIGlzIHRvIGNyZWF0ZSBvdXIgcGxvdCBmaXJzdCBieSB1c2luZyB0aGUgZ2dwbG90MiBwYWNrYWdlLiBUaGVuLCB3ZSBjb252ZXJ0IHRoZSBnZ3Bsb3Qgb2JqZWN0IHRvIGEgJ3Bsb3RseScgb2JqZWN0IHVzaW5nICdnZ3Bsb3RseScuIFRoZXJlJ3MgYSBsb3QgZ29pbmcgb24gaGVyZSBzbyBJJ2xsIGJyZWFrIHNvbWUgb2YgaXQgZG93bi4gVGhlIGdncGxvdCgpIGZ1bmN0aW9uIGluaXRpYWxpc2VzIGEgZ2dwbG90IG9iamVjdCwgd2hpY2ggc2V0cyB1cCB0aGUgZGF0YWZyYW1lIHRoYXQgd2lsbCBiZSB1c2VkIGZvciB0aGUgcGxvdCBhbmQgc3BlY2lmaWVzIHRoZSBhZXN0aGV0aWMgbWFwcGluZ3Mgd2hpY2ggZGVzY3JpYmUgaG93IHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBhcmUgbWFwcGVkIHRvIHZpc3VhbCBwcm9wZXJ0aWVzLiBTbywgaW5zaWRlIGFlcygpIHdlIHNwZWNpZnkgb3VyIHggYW5kIHkgY29sdW1ucywgYW5kIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIG1hcCBvdXIgYWdlIGNvbHVtbiB0byBmaWxsIHRoZSBjb2xvdXIgb2YgdGhlIGJhcnMuCgpNZWFud2hpbGUsIGdlb21fYmFyKCkgaXMgdXNlZCB0byBtYWtlIGJhciBjaGFydHMsIHNvIGl0IGFkZHMgdGhlIGJhciBnZW9tZXRyeSB0byB0aGUgcGxvdC4gQW5kIHdlIHNldCBzdGF0IHRvICdpZGVudGl0eScsIHdoaWNoIHRlbGxzICdnZ3Bsb3QnIHRvIHVzZSB0aGUgdmFsdWUgaW4gdGhlIHktYXhpcyBjb2x1bW4gKCdwZXJjZW50YWdlJykgZm9yIHRoZSBoZWlnaHQgb2YgdGhlIGJhcnMuIEJ5IHNldHRpbmcgcG9zaXRpb24gdG8gJ2RvZGdlJyB3ZSBlbnN1cmUgdGhhdCB0aGUgYmFycyBhcmUgcGxhY2VkIG5leHQgdG8gZWFjaCBvdGhlci4gCgpGaW5hbGx5LCBsYWJzKCkgaXMgdXNlZCB0byBhZGQgb3IgbW9kaWZ5IGxhYmVscywgYW5kIHRoZW1lIGlzIHVzZWQgdG8gY3VzdG9taXNlIG5vbi1kYXRhIHBhcnRzIG9mIHRoZSBwbG90IGxpa2UgdGV4dCwgbGVnZW5kLCBheGVzLiBBbmQgc2NhbGVfZmlsbF9kaXNjcmV0ZSgpIGNvbnRyb2xzIHRoZSBjb2xvdXIgc2NhbGVzIGFuZCBoZXJlIHdlIHVzZSB0aGUgbmFtZSBwYXJhbWV0ZXIgdG8gbGFiZWwgb3VyIGxlZ2VuZCAiQWdlIi4gCgpUTERSOiB3ZSdyZSB1c2luZyB0aGUgKyBvcGVyYXRvciBhbmQgZ2dwbG90IGZ1bmN0aW9ucyB0byBidWlsZCB1cG9uIHRoZSBiYXNlIGdncGxvdCBvYmplY3QsIGxheWVyaW5nIG9uIGFlc3RoZXRpYyBtYXBwaW5ncywgZ2VvbWV0cmllcywgbGFiZWxzLCBldGMuCgpgYGB7cn0KcCA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gZ2VuZGVyX2lkZW50aXR5LCB5ID0gcGVyY2VudGFnZSwgZmlsbCA9IGFnZSwKICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUoJ09ic2VydmF0aW9uOicsIG9ic2VydmF0aW9uKSkpICsgICMgSW5jbHVkZSBvYnNlcnZhdGlvbiBpbmZvCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGxhYnModGl0bGUgPSAnRGlzdHJpYnV0aW9uIG9mIEdlbmRlciBJZGVudGl0eSBDYXRlZ29yaWVzIEFtb25nIEFnZSBHcm91cHMnLAogICAgICAgeCA9ICdHZW5kZXIgSWRlbnRpdHknLCB5ID0gJ1BlcmNlbnRhZ2UnKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiQWdlIikKCiMgTGV0J3MgdGFrZSBhIGxvb2sgYXQgb3VyIHN0YXRpYyBncmFwaApwCmBgYAoKSG1tIG9rYXkuIE5vdCB0b28gc2hhYmJ5LCBidXQgd2UncmUgZGVmaW5pdGVseSBnb2luZyB0byBoYXZlIHRvIGRvIHNvbWV0aGluZyBhYm91dCBvdXIgeC1heGlzIGxhYmVscywgYXMgcmlnaHQgbm93IGV2ZXJ5dGhpbmcgaXMgcHJldHR5IGNsdXR0ZXJlZC4gTWF5YmUgd2UgY291bGQgcm90YXRlIHRoZW0sIG9yIGp1c3QgcmVuYW1lIHRoZW0uIFdlJ2xsIGdldCByb3VuZCB0byBpdC4gQnV0IGZvciBub3csIGxldCdzIG1ha2UgdGhpcyB0aGluZyBpbnRlcmFjdGl2ZS4KCmBgYHtyfQojIENvbnZlcnQgZ2dwbG90IG9iamVjdCB0byBhIHBsb3RseSBvYmplY3QgZm9yIGludGVyYWN0aXZpdHkKZmlnIDwtIGdncGxvdGx5KHAsIHRvb2x0aXAgPSBjKCJ5IiwgImZpbGwiLCAidGV4dCIpKSAgIyBTcGVjaWZ5IHRvb2x0aXAgY29tcG9uZW50cwoKCiMgTGV0J3MgY2hlY2sgaXQgb3V0CmZpZwpgYGAKCiMjIFRvb2x0aXBzIAoKV2hlbiB1c2luZyBkaWZmZXJlbnQgUiBsaWJyYXJpZXMgZ2VhcmVkIHRvd2FyZHMgaW50ZXJhY3RpdmUgdmlzdWFsaXNhdGlvbnMsIHlvdSdsbCBvZnRlbiBjb21lIGFjcm9zcyAndG9vbHRpcHMnLiBUaGVzZSBhcmUgc21hbGwgYm94ZXMgdGhhdCBwcm92aWRlIGluZm9ybWF0aW9uIHdoZW4gYSB1c2VyIGhvdmVycyBvdmVyIGEgcGFydCBvZiBhIGRhdGEgdmlzdWFsaXNhdGlvbiBzdWNoIGFzOiBhIHBvaW50IG9uIGEgZ3JhcGgsIGEgYmFyIGluIGEgYmFyIGNoYXJ0LCBvciBhIHNlZ21lbnQgaW4gYSBwaWUgY2hhcnQuIFRoZXkgYXJlIHVzZWQgdG8gZGlzcGxheSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGFib3V0IHRoZSBkYXRhIHBvaW50IG9yIG9iamVjdCwgcHJvdmlkaW5nIG1vcmUgY29udGV4dCB3aXRob3V0IGNsdXR0ZXJpbmcgdXAgdGhlIGNoYXJ0LiAKCmBgYHtyfQojIFNldCB0aGUgbGV2ZWxzIG9mIHRoZSBmYWN0b3IgdG8gdGhlIG9yZGVyIHlvdSB3YW50IHRoZW0gdG8gYXBwZWFyCmRmJGdlbmRlcl9pZGVudGl0eSA8LSBmYWN0b3IoZGYkZ2VuZGVyX2lkZW50aXR5LCBsZXZlbHMgPSBjKAogICJHZW5kZXIgaWRlbnRpdHkgdGhlIHNhbWUgYXMgc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGgiLAogICJHZW5kZXIgaWRlbnRpdHkgZGlmZmVyZW50IGZyb20gc2V4IHJlZ2lzdGVyZWQgYXQgYmlydGggYnV0IG5vIHNwZWNpZmljIGlkZW50aXR5IGdpdmVuIiwKICAiVHJhbnMgd29tYW4iLAogICJUcmFucyBtYW4iLAogICJBbGwgb3RoZXIgZ2VuZGVyIGlkZW50aXRpZXMiLAogICJOb3QgYW5zd2VyZWQiCikpCgojIEdlbmVyYXRlIHRoZSBwbG90bHkgZmlndXJlCmZpZyA8LSBnZ3Bsb3RseShwKQoKIyBTcGVjaWZ5IGN1c3RvbSB0aWNrIGxhYmVscyB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIHRpY2sgdmFsdWVzCmZpZyA8LSBmaWcgJT4lCiAgbGF5b3V0KAogICAgdGl0bGUgPSBsaXN0KHRleHQgPSAnRGlzdHJpYnV0aW9uIG9mIEdlbmRlciBJZGVudGl0eSBDYXRlZ29yaWVzIEFtb25nIEFnZSBHcm91cHMnLCB4ID0gMC41KSwKICAgIHhheGlzID0gbGlzdCgKICAgICAgdGl0bGUgPSAnR2VuZGVyIElkZW50aXR5JywKICAgICAgdGlja3ZhbHMgPSBsZXZlbHMoZGYkZ2VuZGVyX2lkZW50aXR5KSwgICMgU2V0IHRpY2t2YWxzIHRvIGZhY3RvciBsZXZlbHMKICAgICAgdGlja3RleHQgPSBjKAogICAgICAgICJDaXNnZW5kZXIiLCAKICAgICAgICAiR2VuZGVyIGlkZW50aXR5IGRpZmZlcmVudCBmcm9tIHNleCIsCiAgICAgICAgIlRyYW5zIHdvbWFuIiwKICAgICAgICAiVHJhbnMgbWFuIiwKICAgICAgICAiQWxsIG90aGVyIGlkZW50aXRpZXMiLAogICAgICAgICJOb3QgYW5zd2VyZWQiCiAgICAgICkKICAgICksCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnUGVyY2VudGFnZScpLAogICAgbGVnZW5kID0gbGlzdChvcmllbnRhdGlvbiA9ICJ2IiwgeWFuY2hvciA9ICJ0b3AiLCB5ID0gLTAuMywgeGFuY2hvciA9ICJjZW50ZXIiLCB4ID0gMSkKICApCgpmaWcKYGBgCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkNtZCtPcHRpb24rSSouCgpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkNtZCtTaGlmdCtLKiB0byBwcmV2aWV3IHRoZSBIVE1MIGZpbGUpLiAKClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4KCg==